package com.zhou.org.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhou.framework.util.SessionUtil;
import com.zhou.framework.util.SettingUtil;
import com.zhou.framework.util.UserUtil;
import com.zhou.org.common.RightKeyEnum;
import com.zhou.util.*;
import com.zhou.message.service.AttachmentService;
import com.zhou.org.common.EnumConstant;
import com.zhou.org.common.OrgUtil;
import com.zhou.org.common.RightUtil;
import com.zhou.org.dao.Org;
import com.zhou.org.dao.OrgRelation;
import com.zhou.org.dao.User;
import com.zhou.org.dto.UserQueryDto;
import com.zhou.org.dto.UserUpdateDto;
import com.zhou.org.dto.WechatLoginRequest;
import com.zhou.org.mapper.OrgMapper;
import com.zhou.org.mapper.OrgRelationMapper;
import com.zhou.org.mapper.RightMapper;
import com.zhou.org.mapper.UserMapper;
import com.zhou.org.service.UserPasswordRecordService;
import com.zhou.org.service.UserService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author lang.zhou
 * @since 2022-08-23
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService, ApplicationRunner {
    @Autowired
    private OrgMapper orgMapper;
    @Autowired
    private OrgRelationMapper orgRelationMapper;
    @Autowired
    private RightMapper rightMapper;
    @Autowired
    private AttachmentService attachmentService;

    @Autowired
    private UserPasswordRecordService passwordRecordService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addUser(User user) {
        if(StringTool.isBlank(user.getPassword())){
            String defaultPassword = SettingUtil.getDefaultPassword();
            String salt = StringTool.uuid();
            String hex = Md5Util.hex(defaultPassword, salt);
            user.setSalt(salt);
            user.setPassword(hex);
            user.setCreateDt(new Date());
        }
        this.save(user);
        Org org = new Org();
        org.setOrgName(user.getRealName());
        org.setUserId(user.getUserId());
        org.setOrgType(EnumConstant.ORG_TYPE_USER);
        orgMapper.insert(org);
        user.setOrgId(org.getOrgId());
        updateById(user);
        return org.getOrgId();
    }

    @Override
    public User getUserDto(int userId) {
        LambdaQueryWrapper<User> query = this.createInfoQuery();
        query.eq(User::getUserId,userId);
        return baseMapper.selectOne(query);
    }

    @Override
    public User getUserDtoByOrgId(int orgId) {
        LambdaQueryWrapper<User> query = this.createInfoQuery();
        query.eq(User::getOrgId,orgId);
        return baseMapper.selectOne(query);
    }

    @Override
    public User getSuperior(Integer orgId) {
        if(NumberUtil.upper0(orgId)){
            return baseMapper.getSuperior(orgId);
        }
        return null;
    }

    @Override
    public User getByOpenId(String openId) {
        LambdaQueryWrapper<User> wrap = new LambdaQueryWrapper<>();
        wrap.eq(User::getWxCode,openId);
        User u = baseMapper.selectOne(wrap);
        return u;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addUserWidthOrg(User user, int orgId) {
        this.save(user);
        Org org = new Org();
        org.setOrgName(user.getRealName());
        org.setUserId(user.getUserId());
        org.setOrgType(EnumConstant.ORG_TYPE_USER);
        orgMapper.insert(org);
        //绑定组织结构关系
        Org p = orgMapper.selectById(orgId);
        if(p != null){
            OrgRelation relation = new OrgRelation();
            relation.setFromId(orgId);
            relation.setToId(org.getOrgId());
            orgRelationMapper.insert(relation);
        }
        user.setOrgId(org.getOrgId());
        this.updateById(user);
        return org.getOrgId();
    }

    @Override
    public Org getOrgByUserId(Integer userId){
        LambdaQueryWrapper<Org> query = new LambdaQueryWrapper<>();
        query.eq(Org::getUserId,userId);
        return orgMapper.selectOne(query);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeUser(User user) {
        this.updateById(user);
        Org org = this.getOrgByUserId(user.getUserId());
        if(org == null){
            org = new Org();
            org.setOrgName(user.getRealName());
            org.setUserId(user.getUserId());
            org.setOrgType(EnumConstant.ORG_TYPE_USER);
            orgMapper.insert(org);
        }else{
            if(!user.getRealName().equals(org.getOrgName())){
                org.setOrgName(user.getRealName());
                orgMapper.updateById(org);
            }
        }
    }

    @Override
    public void unlock(Collection<Integer> userIds) {
        if(ObjectUtil.isNotEmpty(userIds)){
            LambdaUpdateWrapper<User> w = new LambdaUpdateWrapper<>();
            w.in(User::getUserId,userIds);
            w.set(User::getLocked,0);
            update(w);
        }
    }

    @Override
    public void lock(Collection<Integer> userIds) {
        if(ObjectUtil.isNotEmpty(userIds)){
            LambdaUpdateWrapper<User> w = new LambdaUpdateWrapper<>();
            w.in(User::getUserId,userIds);
            w.set(User::getLocked,1);
            update(w);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveUserWidthOrg(User user, List<Integer> newOrgIdList) {
        if(NumberUtil.upper0(user.getUserId())){
            changeUser(user);
        }else{
            addUser(user);
        }
        int orgId = user.getOrgId();
        orgRelationMapper.deleteOrgRelationByOrgId(orgId);
        for (Integer oid : newOrgIdList) {
            OrgRelation relation = new OrgRelation();
            relation.setFromId(oid);
            relation.setToId(orgId);
            orgRelationMapper.insert(relation);
        }
        RightUtil.refreshUserMenuCache(RightKeyEnum.MENU,"YES",orgId);
        OrgUtil.refreshCache();
    }

    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class)
    public User saveUser(UserUpdateDto dto) {
        User user;
        if(NumberUtil.upper0(dto.getUserId())){
            user = getById(dto.getUserId());
            dto.setUsername(user.getUsername());
            Assert.isTrue(!Objects.equals(dto.getSuperior(),dto.getUserId()),"行政上级不能是自己");
        }else{
            user = new User();
            User cu = getByUserName(dto.getUsername());
            if(cu != null && !Objects.equals(cu.getUserId(),dto.getUserId())){
                Assert.error("该用户名已存在！");
            }
        }
        BeanUtils.copyProperties(dto,user,"avatar");

        MultipartFile head = dto.getHead();
        if(head != null){
            String avatar = this.saveUserHead(head.getBytes(), head.getOriginalFilename(),dto.getUserId());
            user.setRelativeAvatar(avatar);
        }

        log.info("avatar：{}",user.getRelativeAvatar());

        this.saveUserWidthOrg(user,dto.getNewOrgIdList());

        return user;
    }

    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String saveUserHead(byte[] bytes,String filename, Integer userId){
        int attachId = attachmentService.saveHead(userId, bytes, filename);
        String url = "/sys/attach/pubGet?id=" + attachId + "&dt=" + System.currentTimeMillis();
        LambdaUpdateWrapper<User> w = new LambdaUpdateWrapper<>();
        w.set(User::getRelativeAvatar,url);
        w.eq(User::getUserId,userId);
        update(w);
        return url;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeUserPassword(Integer userId, String oldHex, String newHex) {
        passwordRecordService.saveRecord(userId,oldHex,newHex);
        LambdaUpdateWrapper<User> w = new LambdaUpdateWrapper<>();
        w.set(User::getPassword,newHex);
        w.eq(User::getUserId,userId);
        update(w);
    }

    @Override
    public void changeDescription(String description, int userId) {
        baseMapper.changeDescription(description,userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUser(List<Integer> ids) {
        if(ObjectUtil.isNotEmpty(ids)){
            for(Integer id : ids){
                //不删除管理员
                if(id!=1){
                    User user = getById(id);
                    if(user != null){
                        log.warn("删除用户："+user.toString());
                        //删除用户
                        baseMapper.deleteById(id);
                        //删除绑定的组织架构对象
                        orgMapper.deleteOrgByUserId(id);
                        //删除组织架构关系
                        orgRelationMapper.deleteOrgRelationByOrgId(user.getOrgId());
                        //删除权限
                        rightMapper.deleteRightByOrgId(user.getOrgId());
                        //刷新用户菜单权限缓存
                        RightUtil.refreshUserMenuCache(RightKeyEnum.MENU,"YES",user.getOrgId());
                    }

                }
            }
        }
        //刷新组织结构缓存
        OrgUtil.refreshCache();
    }

    @Override
    public void resetPassword(List<Integer> ids) {
        if(ObjectUtil.isNotEmpty(ids)){
            String defaultPassword = SettingUtil.getDefaultPassword();
            for (Integer id : ids) {
                User user = getById(id);
                String salt = StringTool.uuid();
                String hex = Md5Util.hex(defaultPassword, salt);
                user.setSalt(salt);
                user.setPassword(hex);
                updateById(user);
            }
        }
    }

    @Override
    public List<User> getUsersByIdAndName(List<Integer> ids, String userName) {
        LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>();
        query.in(User::getUserId,ids);
        if(StringTool.isNotBlank(userName)){
            query.like(User::getUsername,"%"+userName+"%");
        }
        return list(query);
    }

    @Override
    public List<Map<String, Object>> getUsersMapByIdAndName(List<Integer> ids, String userName) {
        LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>();
        query.in(User::getUserId,ids);
        if(StringTool.isNotBlank(userName)){
            query.like(User::getUsername,"%"+userName+"%");
        }
        return new ArrayList<>(0);
    }

    @Override
    public List<User> getUsersByOrgId(List<Integer> ids) {
        QueryWrapper<User> query = new QueryWrapper<>();
        query.in("orgid",ids);
        return list(query);
    }

    @Override
    public User getUserByOrgId(Integer orgId) {
        LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>();
        query.eq(User::getOrgId,orgId);
        return baseMapper.selectOne(query);
    }

    @Override
    public Map<Integer, Integer> getOrgIdList(List<Integer> idList) {
        List<Map<String,Object>> list = baseMapper.getOrgIdList(idList);
        Map<Integer, Integer> res = new HashMap<>(list.size());
        for (Map<String, Object> map : list) {
            int userId = MapUtils.getIntValue(map,"user_id",0);
            int orgId = MapUtils.getIntValue(map,"orgid",0);
            res.put(userId,orgId);
        }
        return res;
    }

    @Override
    public User getByUserName(String username) {
        List<User> list = baseMapper.getUserByUserName(username);
        return ListUtil.getFirst(list);
    }

    private LambdaQueryWrapper<User> createInfoQuery(){
        LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>();
        query.select(User::getUserId,User::getUsername,User::getRealName,User::getSex,User::getEmail,User::getPhone,User::getCreateDt
                ,User::getWxCode,User::getRelativeAvatar,User::getLocked,User::getOrgId,User::getDescription,User::getSuperior,User::getLoginMode);
        return query;
    }
    @Override
    public List<User> getPageList(UserQueryDto dto) {
        LambdaQueryWrapper<User> query = this.createInfoQuery();
        if(dto.getLocked() != null){
            query.eq(User::getLocked,dto.getLocked());
        }
        if(StringTool.isNotBlank(dto.getUserName())){
            query.and(userQueryWrapper -> userQueryWrapper.like(User::getUsername,"%"+dto.getUserName()+"%").or().like(User::getRealName,"%"+dto.getUserName()+"%"));
        }
        query.orderByAsc(User::getUsername);
        return list(query);
    }

    @Override
    public List<User> userAuto(String userName) {
        LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>();
        query.select(User::getUsername,User::getRealName,User::getUserId,User::getOrgId);
        query.eq(User::getLocked,0);
        query.and(userQueryWrapper -> userQueryWrapper.like(User::getUsername,"%"+userName+"%").or().like(User::getRealName,"%"+userName+"%"));
        return list(query);
    }

    @Override
    public User createWechatUser(WechatLoginRequest r){
        String defaultPwd = SettingUtil.getDefaultPassword();
        //String[] keys = UserUtil.createKey();
        User user = new User();
        user.setUsername(UserUtil.randomUsername());
        user.setWxCode(r.getOpenId());
        user.setSalt(StringTool.uuid());
        user.setCreateDt(new Date());
        user.setPassword(Md5Util.hex(defaultPwd,user.getSalt()));
        user.setRealName(r.getNickName());
        save(user);
        log.info("new user["+user.getUsername()+"]");
        return user;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        SessionUtil.clearCache();
    }
}
